// Promise
// 一种处理异步代码（而不会陷入回调地狱）的方式
// 异步函数在底层使用了 promise ，因此了解 promise 的工作方式是了解 async 和 await 的基础
// Promise 对象代表一个异步操作，有 pending(进行中) fulfilled(成功) rejected(失败) 三个状态

// 运作过程：
//    当 Promise 被调用后，进入 pending 状态。这意味着调用的函数会继续执行，而 Promise 仍处于 pending 知道解决为止，从而为调用函数提供所请求的任何数据
//    被创建的 Promise 最终以 fulfilled 或 rejected 状态结束，并在完成时调用相应的回调函数（传给 then 和 catch）

// 特点：
//    执行 resolve、reject ，Promise 状态分别会变成 fulfilled、rejected，其中 Promise 中有 throw 相当于执行 reject
//    Promise 没有执行 resolve、reject、throw， Promise 状态不变为 pending
//    pending 状态下的 Promise 不会执行回调函数 then()
//    Promise 必须传入一个执行函数，否则会报错
//    Promise 一旦发生状态改变就是永久的
//    在本轮 事件循环 运行完成之前，回调函数是不会被调用的。
//    即使异步操作已经完成（成功或失败），在这之后通过 then() 添加的回调函数也会被调用。
//    通过多次调用 then() 可以添加多个回调函数，它们会按照插入顺序进行执行。

class myPromise {
  static PENDING = 'pending'
  static FULIIIED = 'fulilled'
  static REJECTED = 'rejected'
  // constructor 指向实力原型的构造函数
  constructor(func) {
    this.PromiseState = myPromise.PENDING
    this.PromiseResult = null
    this.onFulfilledCallbacks = [] // 保存成功回调
    this.onRejectedCallbacks = [] // 保存失败回调
    try {
      // new 一个新实例的时候执行的是 constructor中的内容，即 constructor 中的this指向新实例，
      // 但我们在新实例被创建后的外部使用 resolve ，就相当于不在 class 内部使用该 this
      // 所以要给 resolve 和 reject 绑定 this 为当前的实例对象，待创建实例调用后才执行 所以使用 bind
      func(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      // 这里不需要给 reject 方法进行 this 绑定了，这里是直接执行而不是创建实例后再执行
      this.reject(error)
    }
  }
  // then 是在创建实例后再进行调用的，所以要再创建一个类方法
  // Promise 规定then方法里面的两个参数如果不是函数的话就要被忽略

  // then方法本身会返回一个新的Promise对象，返回一个新的Promise以后它就有自己的then方法，这样就能实现无限的链式
  // 不论 promise1 被 resolve() 还是被 reject() 时 promise2 都会执行 Promise 解决过程：[[Resolve]](promise2, x)
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason } // 不理解
    
    const promise2 = new myPromise((resolve, reject) => {
      if (this.PromiseState === myPromise.PENDING) {
        // 为了保留 then 里的函数，我们创建 数组来保存函数
        // 为很么使用数组保存回调？ 一个 Promise 实例可能会多次 then，即链式调用，而数组是先入先出顺序
        this.onFulfilledCallbacks.push(() => {
          try {
            let x = onFulfilled(this.PromiseResult)
            // 如果 onFulfilled 或者 onRejected 抛出一个异常 e ，则 promise2 必须拒绝执行，并返回拒因 e
            resolvePromise(promise2, x, resolve, reject)
          } catch(e) {
              reject(e)
          }
        })
        this.onRejectedCallbacks.push(() => {
          try {
            let x = onRejected(this.PromiseResult)
            resolvePromise(promise2, x, resolve, reject)
          } catch(e) {
              reject(e)
          }
        })
      }
      // onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用 
      // 平台代码指的是引擎、环境以及 promise 的实施代码
      // 实践中要确保 onFulfilled 和 onRejected 方法异步执行，且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
      // 这个事件队列可以采用“宏任务（macro-task）”机制，比如setTimeout 或者 setImmediate
      // 也可以采用“微任务（micro-task）”机制来实现， 比如 MutationObserver 或者process.nextTick
      else if (this.PromiseState === myPromise.FULIIIED) {
        // 设置异步执行，在 if 中添加以确保状态不处于pending
        setTimeout(() => {
          try {
            let x = onFulfilled(this.PromiseResult)
            resolvePromise(promise2, x, resolve, reject)
          } catch(e) {
              reject(e) // 捕获前面onFulfilled中抛出的异常
          }
          
        })
      }
      else if (this.PromiseState === myPromise.REJECTED) {
        // 设置异步执行，在 if 中添加以确保状态不处于pending
        setTimeout(() => {
          try {
            let x = onRejected(this.PromiseResult)
            resolvePromise(promise2, x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        })
      }
    })
    return promise2
  }

  resolve(result) {
    if (this.PromiseState === myPromise.PENDING) {
      setTimeout(() => {
        this.PromiseState = myPromise.FULIIIED
        this.PromiseResult = result
        this.onFulfilledCallbacks.forEach(callback => {
          callback(result)
        })
      })
    }
  }
  reject(reason) {
    if (this.PromiseState === myPromise.PENDING) {
      setTimeout(() => {
        this.PromiseState = myPromise.REJECTED
        this.PromiseResult = reason
        this.onRejectedCallbacks.forEach(callback => {
          callback(reason)
        })
      });
      
    }
  }
}
/**
 * 对resolve()、reject() 进行改造增强 针对resolve()和reject()中不同值情况 进行处理
 * @param  {promise} promise2 promise1.then方法返回的新的promise对象
 * @param  {[type]} x         promise1中onFulfilled或onRejected的返回值
 * @param  {[type]} resolve   promise2的resolve方法
 * @param  {[type]} reject    promise2的reject方法
 */
function resolvePromise(promise2, x, resolve, reject) {
  // 如果 promise 和 x 指向同一对象，以 TypeError 为因(循环引用报错)拒绝执行 promise
  if (x === promise2) { return reject(new TypeError('Chaining cycle detected for promise')) }

  // 2.3.2 如果 x 为 Promise ，则使 promise2 接受 x 的状态
  if (x instanceof myPromise) {
    if (x.PromiseState === myPromise.PENDING) {
      /**
        * 2.3.2.1 如果 x 处于等待态， promise 需保持为等待态直至 x 被执行或拒绝
        *         注意"直至 x 被执行或拒绝"这句话，
        *         这句话的意思是：x 被执行x，如果执行的时候拿到一个y，还要继续解析y
        */
      x.then(y => {
        resolvePromise(promise2, y, resolve, reject)
      }, reject)
    }else if (x.PromiseState === myPromise.FULFILLED){
      // 2.3.2.2 如果 x 处于执行态，用相同的值执行 promise
      resolve(x.PromiseResult);
    }else if (x.PromiseState === myPromise.REJECTED){
      // 2.3.2.2 如果 x 处于拒绝行态，用相同的据因拒绝 promise
      reject(x.PromiseResult)
    }else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) {
      // 2.3.3 如果 x 为对象或函数
      try {
        // 2.3.3.1 把 x.then 赋值给 then
        var then = x.then;
      } catch (e) {
        // 2.3.3.2 如果取 x.then 的值时抛出错误 e ，则以 e 为据因拒绝 promise
        return reject(e)
      }
    }

    /**
      * 2.3.3.3 
      * 如果 then 是函数，将 x 作为函数的作用域 this 调用之。
      * 传递两个回调函数作为参数，
      * 第一个参数叫做 `resolvePromise` ，第二个参数叫做 `rejectPromise`
      */
    if (typeof then === 'function') {
      // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用，或者被同一参数调用了多次，则优先采用首次调用并忽略剩下的调用
      let called = false // 避免多次调用
      try {
        then.call(
          x,
          // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用，则运行 [[Resolve]](promise, y)
          y => {
            if (called) { reutrn }
            called = true
            resolvePromise(promise2, y, resolve, reject)
          },
          // 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用，则以据因 r 拒绝 promise
          r => {
            if (called) { return }
            called = true
            reject(r)
          }
        )
      } catch (e) {
        /**
          * 2.3.3.3.4 如果调用 then 方法抛出了异常 e
          * 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用，则忽略之
          */
        if (called) { return }
        //  2.3.3.3.4.2 否则以 e 为据因拒绝 promise
        reject(e)
      }
    } else {
      // 2.3.3.4 如果 then 不是函数，以 x 为参数执行 promise
      resolve(x)
    }
  } else {
    // 2.3.4 如果 x 不为对象或者函数，以 x 为参数执行 promise
    return resolve(x);
  }
}
// setTimeout()属于宏任务 promise.then()属于微任务
// 当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件，然后再去宏任务队列中取出一个事件。
// 同一次事件循环中，微任务永远在宏任务之前执行。



// 按步分析 注释加持版
class myPromise {
  // 为了统一用static创建静态属性，用来管理状态
  static PENDING = 'pending';
  static FULFILLED = 'fulfilled';
  static REJECTED = 'rejected';

  // 构造函数：通过new命令生成对象实例时，自动调用类的构造函数
  constructor(func) { // 给类的构造方法constructor添加一个参数func
      this.PromiseState = myPromise.PENDING; // 指定Promise对象的状态属性 PromiseState，初始值为pending
      this.PromiseResult = null; // 指定Promise对象的结果 PromiseResult
      this.onFulfilledCallbacks = []; // 保存成功回调
      this.onRejectedCallbacks = []; // 保存失败回调
      try {
          /**
           * func()传入resolve和reject，
           * resolve()和reject()方法在外部调用，这里需要用bind修正一下this指向
           * new 对象实例时，自动执行func()
           */
          func(this.resolve.bind(this), this.reject.bind(this));
      } catch (error) {
          // 生成实例时(执行resolve和reject)，如果报错，就把错误信息传入给reject()方法，并且直接执行reject()方法
          this.reject(error)
      }
  }

  resolve(result) { // result为成功态时接收的终值
      // 只能由pedning状态 => fulfilled状态 (避免调用多次resolve reject)
      if (this.PromiseState === myPromise.PENDING) {
          /**
           * 为什么resolve和reject要加setTimeout?
           * 2.2.4规范 onFulfilled 和 onRejected 只允许在 execution context 栈仅包含平台代码时运行.
           * 注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行，且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
           * 这个事件队列可以采用“宏任务（macro-task）”机制，比如setTimeout 或者 setImmediate； 也可以采用“微任务（micro-task）”机制来实现， 比如 MutationObserver 或者process.nextTick。 
           */
          setTimeout(() => {
              this.PromiseState = myPromise.FULFILLED;
              this.PromiseResult = result;
              /**
               * 在执行resolve或者reject的时候，遍历自身的callbacks数组，
               * 看看数组里面有没有then那边 保留 过来的 待执行函数，
               * 然后逐个执行数组里面的函数，执行的时候会传入相应的参数
               */
              this.onFulfilledCallbacks.forEach(callback => {
                  callback(result)
              })
          });
      }
  }

  reject(reason) { // reason为拒绝态时接收的终值
      // 只能由pedning状态 => rejected状态 (避免调用多次resolve reject)
      if (this.PromiseState === myPromise.PENDING) {
          setTimeout(() => {
              this.PromiseState = myPromise.REJECTED;
              this.PromiseResult = reason;
              this.onRejectedCallbacks.forEach(callback => {
                  callback(reason)
              })
          });
      }
  }

  /**
   * [注册fulfilled状态/rejected状态对应的回调函数] 
   * @param {function} onFulfilled  fulfilled状态时 执行的函数
   * @param {function} onRejected  rejected状态时 执行的函数 
   * @returns {function} newPromsie  返回一个新的promise对象
   */
  then(onFulfilled, onRejected) {
      /**
       * 参数校验：Promise规定then方法里面的两个参数如果不是函数的话就要被忽略
       * 所谓“忽略”并不是什么都不干，
       * 对于onFulfilled来说“忽略”就是将value原封不动的返回，
       * 对于onRejected来说就是返回reason，
       *     onRejected因为是错误分支，我们返回reason应该throw一个Error
       */
      onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
      onRejected = typeof onRejected === 'function' ? onRejected : reason => {
          throw reason;
      };

      // 2.2.7规范 then 方法必须返回一个 promise 对象
      let promise2 = new myPromise((resolve, reject) => {
          if (this.PromiseState === myPromise.FULFILLED) {
              /**
               * 为什么这里要加定时器setTimeout？
               * 2.2.4规范 onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用 注1
               * 这里的平台代码指的是引擎、环境以及 promise 的实施代码。
               * 实践中要确保 onFulfilled 和 onRejected 方法异步执行，且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
               * 这个事件队列可以采用“宏任务（macro-task）”机制，比如setTimeout 或者 setImmediate； 也可以采用“微任务（micro-task）”机制来实现， 比如 MutationObserver 或者process.nextTick。
               */
              setTimeout(() => {
                  try {
                      // 2.2.7.1规范 如果 onFulfilled 或者 onRejected 返回一个值 x ，则运行下面的 Promise 解决过程：[[Resolve]](promise2, x)，即运行resolvePromise()
                      let x = onFulfilled(this.PromiseResult);
                      resolvePromise(promise2, x, resolve, reject);
                  } catch (e) {
                      // 2.2.7.2 如果 onFulfilled 或者 onRejected 抛出一个异常 e ，则 promise2 必须拒绝执行，并返回拒因 e
                      reject(e); // 捕获前面onFulfilled中抛出的异常
                  }
              });
          } else if (this.PromiseState === myPromise.REJECTED) {
              setTimeout(() => {
                  try {
                      let x = onRejected(this.PromiseResult);
                      resolvePromise(promise2, x, resolve, reject);
                  } catch (e) {
                      reject(e)
                  }
              });
          } else if (this.PromiseState === myPromise.PENDING) {
              // pending 状态保存的 resolve() 和 reject() 回调也要符合 2.2.7.1 和 2.2.7.2 规范
              this.onFulfilledCallbacks.push(() => {
                  try {
                      let x = onFulfilled(this.PromiseResult);
                      resolvePromise(promise2, x, resolve, reject)
                  } catch (e) {
                      reject(e);
                  }
              });
              this.onRejectedCallbacks.push(() => {
                  try {
                      let x = onRejected(this.PromiseResult);
                      resolvePromise(promise2, x, resolve, reject);
                  } catch (e) {
                      reject(e);
                  }
              });
          }
      })

      return promise2
  }
}

/**
* 对resolve()、reject() 进行改造增强 针对resolve()和reject()中不同值情况 进行处理
* @param  {promise} promise2 promise1.then方法返回的新的promise对象
* @param  {[type]} x         promise1中onFulfilled或onRejected的返回值
* @param  {[type]} resolve   promise2的resolve方法
* @param  {[type]} reject    promise2的reject方法
*/
function resolvePromise(promise2, x, resolve, reject) {
  // 2.3.1规范 如果 promise 和 x 指向同一对象，以 TypeError 为据因拒绝执行 promise
  if (x === promise2) {
      return reject(new TypeError('Chaining cycle detected for promise'));
  }

  // 2.3.2规范 如果 x 为 Promise ，则使 promise2 接受 x 的状态
  if (x instanceof myPromise) {
      if (x.PromiseState === myPromise.PENDING) {
          /**
           * 2.3.2.1 如果 x 处于等待态， promise 需保持为等待态直至 x 被执行或拒绝
           *         注意"直至 x 被执行或拒绝"这句话，
           *         这句话的意思是：x 被执行x，如果执行的时候拿到一个y，还要继续解析y
           */
          x.then(y => {
              resolvePromise(promise2, y, resolve, reject)
          }, reject);
      } else if (x.PromiseState === myPromise.FULFILLED) {
          // 2.3.2.2 如果 x 处于执行态，用相同的值执行 promise
          resolve(x.PromiseResult);
      } else if (x.PromiseState === myPromise.REJECTED) {
          // 2.3.2.3 如果 x 处于拒绝态，用相同的据因拒绝 promise
          reject(x.PromiseResult);
      }
  } else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) {
      // 2.3.3 如果 x 为对象或函数
      try {
          // 2.3.3.1 把 x.then 赋值给 then
          var then = x.then;
      } catch (e) {
          // 2.3.3.2 如果取 x.then 的值时抛出错误 e ，则以 e 为据因拒绝 promise
          return reject(e);
      }

      /**
       * 2.3.3.3 
       * 如果 then 是函数，将 x 作为函数的作用域 this 调用之。
       * 传递两个回调函数作为参数，
       * 第一个参数叫做 `resolvePromise` ，第二个参数叫做 `rejectPromise`
       */
      if (typeof then === 'function') {
          // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用，或者被同一参数调用了多次，则优先采用首次调用并忽略剩下的调用
          let called = false; // 避免多次调用
          try {
              then.call(
                  x,
                  // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用，则运行 [[Resolve]](promise, y)
                  y => {
                      if (called) return;
                      called = true;
                      resolvePromise(promise2, y, resolve, reject);
                  },
                  // 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用，则以据因 r 拒绝 promise
                  r => {
                      if (called) return;
                      called = true;
                      reject(r);
                  }
              )
          } catch (e) {
              /**
               * 2.3.3.3.4 如果调用 then 方法抛出了异常 e
               * 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用，则忽略之
               */
              if (called) return;
              called = true;

              /**
               * 2.3.3.3.4.2 否则以 e 为据因拒绝 promise
               */
              reject(e);
          }
      } else {
          // 2.3.3.4 如果 then 不是函数，以 x 为参数执行 promise
          resolve(x);
      }
  } else {
      // 2.3.4 如果 x 不为对象或者函数，以 x 为参数执行 promise
      return resolve(x);
  }
}




// 清爽简洁 无注释版
class myPromise {
  static PENDING = 'pending';
  static FULFILLED = 'fulfilled';
  static REJECTED = 'rejected';
  
  constructor(func) {
      this.PromiseState = myPromise.PENDING;
      this.PromiseResult = null;
      this.onFulfilledCallbacks = [];
      this.onRejectedCallbacks = [];
      try {
          func(this.resolve.bind(this), this.reject.bind(this));
      } catch (error) {
          this.reject(error)
      }
  }
  
  resolve(result) {
      if (this.PromiseState === myPromise.PENDING) {
          setTimeout(() => {
              this.PromiseState = myPromise.FULFILLED;
              this.PromiseResult = result;
              this.onFulfilledCallbacks.forEach(callback => {
                  callback(result)
              })
          });
      }
  }
  
  reject(reason) {
      if (this.PromiseState === myPromise.PENDING) {
          setTimeout(() => {
              this.PromiseState = myPromise.REJECTED;
              this.PromiseResult = reason;
              this.onRejectedCallbacks.forEach(callback => {
                  callback(reason)
              })
          });
      }
  }
  
  then(onFulfilled, onRejected) {
      onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
      onRejected = typeof onRejected === 'function' ? onRejected : reason => {
          throw reason;
      };

      let promise2 = new myPromise((resolve, reject) => {
          if (this.PromiseState === myPromise.FULFILLED) {
              setTimeout(() => {
                  try {
                      let x = onFulfilled(this.PromiseResult);
                      resolvePromise(promise2, x, resolve, reject);
                  } catch (e) {
                      reject(e);
                  }
              });
          } else if (this.PromiseState === myPromise.REJECTED) {
              setTimeout(() => {
                  try {
                      let x = onRejected(this.PromiseResult);
                      resolvePromise(promise2, x, resolve, reject);
                  } catch (e) {
                      reject(e)
                  }
              });
          } else if (this.PromiseState === myPromise.PENDING) {
              this.onFulfilledCallbacks.push(() => {
                  try {
                      let x = onFulfilled(this.PromiseResult);
                      resolvePromise(promise2, x, resolve, reject)
                  } catch (e) {
                      reject(e);
                  }
              });
              this.onRejectedCallbacks.push(() => {
                  try {
                      let x = onRejected(this.PromiseResult);
                      resolvePromise(promise2, x, resolve, reject);
                  } catch (e) {
                      reject(e);
                  }
              });
          }
      })

      return promise2
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if (x === promise2) {
      return reject(new TypeError('Chaining cycle detected for promise'));
  }

  if (x instanceof myPromise) {
      if (x.PromiseState === myPromise.PENDING) {
          x.then(y => {
              resolvePromise(promise2, y, resolve, reject)
          }, reject);
      } else if (x.PromiseState === myPromise.FULFILLED) {
          resolve(x.PromiseResult);
      } else if (x.PromiseState === myPromise.REJECTED) {
          reject(x.PromiseResult);
      }
  } else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) {
      try {
          var then = x.then;
      } catch (e) {
          return reject(e);
      }

      if (typeof then === 'function') {
          let called = false;
          try {
              then.call(
                  x,
                  y => {
                      if (called) return;
                      called = true;
                      resolvePromise(promise2, y, resolve, reject);
                  },
                  r => {
                      if (called) return;
                      called = true;
                      reject(r);
                  }
              )
          } catch (e) {
              if (called) return;
              called = true;

              reject(e);
          }
      } else {
          resolve(x);
      }
  } else {
      return resolve(x);
  }
}
